package com.shaozi.nc.data.mongodao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bson.types.ObjectId;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.StringUtils;

import com.mongodb.WriteResult;
import com.shaozi.data.mongo.dao.SZMongoBaseDao;
import com.shaozi.data.mongo.model.ModelBase;
import com.shaozi.data.mq.business.bean.MqTargetUserBean;
import com.shaozi.lib.throwable.SzException;
import com.shaozi.nc.bean.resp.ReqListSecretaryBean;
import com.shaozi.nc.bean.resp.ReqMessageSetReadForTypeBean;
import com.shaozi.nc.bean.resp.ReqSecretaryTypeBadgeBean;
import com.shaozi.nc.bean.resp.ReqSecretaryUnreadMessageTotalBean;
import com.shaozi.nc.bean.resp.RespBaseTypeBadgeBean;
import com.shaozi.nc.bean.resp.RespBriefReportTypeBadgeBean;
import com.shaozi.nc.bean.resp.RespMessageSetReadForTypeBean;
import com.shaozi.nc.bean.resp.RespSecretaryTypeBadgeBean;
import com.shaozi.nc.bean.resp.RespSecretaryUnreadMessageTotalBean;
import com.shaozi.nc.data.model.BriefReportModel;
import com.shaozi.nc.data.model.SecretaryModel;
import com.shaozi.nc.mq.bean.base.CommonExtraBean;
import com.shaozi.utils.date.DateHelper;

/**
 * 通用小秘书继承类
 * 
 * @author lyx
 *
 * @param <T>
 */
public class BaseSecretaryMDao<T extends ModelBase> extends SZMongoBaseDao<T> {

	public BaseSecretaryMDao(MongoTemplate shaoziTemplate) {
		super(shaoziTemplate);
	}

	public void saveModel(SecretaryModel model) throws SzException {
		getMongoTemplate().save(model, getCollectionName(model.getCompanyId()));
	}

	public void saveModels(String companyId, List<SecretaryModel> models) throws SzException {
		if (models == null)
			return;
		getMongoTemplate().insert(models, getCollectionName(companyId));
	}

	/**
	 * Map<String, RespSecretaryTypeBadgeBean>
	 * 
	 * @param reqBean
	 * @return
	 * @throws SzException
	 */
	@Deprecated
	public Map getTypeBadge(ReqSecretaryTypeBadgeBean reqBean) throws SzException {
		Map<String, RespSecretaryTypeBadgeBean> ret = new HashMap<>();
		commonGetTypeBadge(ret, reqBean, SecretaryModel.class);
		return ret;
	}

	protected void commonGetTypeBadge(Map ret, ReqSecretaryTypeBadgeBean reqBean, Class model) throws SzException {
		Map<String, Integer> only = new HashMap<>();
		if ((reqBean.getModuleTypes() != null) && (reqBean.getModuleTypes().length > 0)) {
			for (String type : reqBean.getModuleTypes()) {
				if (only.containsKey(type))
					continue;
				only.put(type, 1);
				commonGetTypeBadgePut("type", ret, type, model, reqBean);
			}
		}
		if ((reqBean.getSourceTypes() != null) && (reqBean.getSourceTypes().length > 0)) {
			for (String sourceType : reqBean.getSourceTypes()) {
				if (only.containsKey(sourceType))
					continue;
				only.put(sourceType, 1);
				commonGetTypeBadgePut("sourceType", ret, sourceType, model, reqBean);
			}
		}
	}

	private void commonGetTypeBadgePut(String key, Map ret, String type, Class model, ReqSecretaryTypeBadgeBean reqBean)
			throws SzException {
		if (model.isInstance(SecretaryModel.class)) {
			RespSecretaryTypeBadgeBean respBean = getBadgeByTypes(key, reqBean, type);
			if (respBean == null)
				return;
			ret.put(type, respBean);
		} else if (model.isInstance(BriefReportModel.class)) {
			RespBriefReportTypeBadgeBean respBean = getBadgeByTypesBriefReport(key, reqBean, type);
			if (respBean == null)
				return;
			ret.put(type, respBean);
		}
	}

	public RespBaseTypeBadgeBean unreadMessageTotal(ReqSecretaryUnreadMessageTotalBean reqBean) throws SzException {
		SecretaryModel model = findOneByParams(reqBean);
		if (model == null)
			return null;
		RespSecretaryUnreadMessageTotalBean respBean = new RespSecretaryUnreadMessageTotalBean();
		respBean.setSecretary(model);

		int count = unreadMessageTotalCount(reqBean);
		respBean.setCount(count);
		return respBean;
	}

	/**
	 * 获取一条 先返回未读的 没有未读才会返回已读的代码
	 * 
	 * @param reqBean
	 * @return
	 * @throws SzException
	 */
	protected SecretaryModel findOneByParams(ReqSecretaryUnreadMessageTotalBean reqBean) throws SzException {
		Criteria c = commonCriteriaByUserId(reqBean, true);
		Query query = new Query(c);
		Sort sort = new Sort(new Sort.Order(Direction.DESC, "createTime"));
		query.with(sort);
		SecretaryModel model = (SecretaryModel) getMongoTemplate().findOne(query, getClassSon(),
				getCollectionName(reqBean.getCompanyId()));
		if (model == null) {
			c = commonCriteriaByUserId(reqBean, false);
			query = new Query(c);
			query.with(sort);
			model = (SecretaryModel) getMongoTemplate().findOne(query, getClassSon(),
					getCollectionName(reqBean.getCompanyId()));
			if (model == null) {
				return null;
			}
		}
		tidySecretaryByUser(model, reqBean.getUserId());
		return model;
	}

	private Criteria commonCriteriaByUserId(ReqSecretaryUnreadMessageTotalBean reqBean, boolean isQueryAddIsRead) {
		Criteria childUserC = new Criteria("userId").is(reqBean.getUserId());
		childUserC.and("isDel").is(0);
		if (isQueryAddIsRead) {
			childUserC.and("isRead").is(MqTargetUserBean.isRead_no);
		}
		Criteria c = new Criteria();
		c.and("users").elemMatch(childUserC);
		c.and("isDel").is(0);
		return c;
	}

	protected int unreadMessageTotalCount(ReqSecretaryUnreadMessageTotalBean reqBean) throws SzException {
		Criteria childUserC = new Criteria("userId").is(reqBean.getUserId());
		childUserC.and("isRead").is(0);
		childUserC.and("isDel").is(0);
		Criteria c = new Criteria();
		c.and("users").elemMatch(childUserC);
		c.and("isDel").is(0);
		Query query = new Query(c);
		int count = (int) getMongoTemplate().count(query, getClassSon(), getCollectionName(reqBean.getCompanyId()));
		return count;
	}

	/**
	 * 被更新后的时间戳
	 * 
	 * @param companyId
	 * @param id
	 * @param userId
	 * @return
	 * @throws SzException
	 */
	public boolean makeSecretaryRead(String companyId, String[] id, String userId,
			RespMessageSetReadForTypeBean respBean) throws SzException {
		if ((id == null) || (id.length == 0))
			return false;
		List<ObjectId> ids = new ArrayList<>();
		for (String d : id) {
			ids.add(new ObjectId(d));
		}
		Criteria citem = new Criteria("userId").is(userId);
		citem.and("isRead").is(0);
		citem.and("isDel").is(0);

		Criteria c = new Criteria("_id").in(ids);
		// c.and("users.userId").is(userId);
		c.and("users").elemMatch(citem);
		Query query = new Query(c);
		Update update = new Update();
		long now = System.currentTimeMillis();

		update.set("users.$.isRead", 1);
		update.set("users.$.readTime", now);
		update.set("users.$.updateTime", now);
		WriteResult wr = getMongoTemplate().updateMulti(query, update, getClassSon(), getCollectionName(companyId));
		if (wr != null) {
			respBean.setCount(wr.getN());
			respBean.setUpdateTime(now);
			return true;
		}
		return false;
	}

	/**
	 * 被更新后的时间戳
	 * 
	 * @param reqBean
	 * @return
	 * @throws SzException
	 */
	public boolean makeSecretaryRead(ReqMessageSetReadForTypeBean reqBean, RespMessageSetReadForTypeBean respBean)
			throws SzException {
		Criteria citem = new Criteria("userId").is(reqBean.getUserId());
		citem.and("isRead").is(0);
		citem.and("isDel").is(0);
		Criteria c = new Criteria();
		c.and("users").elemMatch(citem);
		c.and("isDel").is(0);
		criteriaNotifyType(c, reqBean.getNotifyType());
		if ((reqBean.getModuleType() != null) && (reqBean.getModuleType() != 0)) {
			c.and("type").is(reqBean.getModuleType());
		}

		long now = System.currentTimeMillis();
		Query query = new Query(c);
		Update update = new Update();

		update.set("users.$.isRead", 1);
		update.set("users.$.readTime", now);
		update.set("users.$.updateTime", now);
		WriteResult wr = getMongoTemplate().updateMulti(query, update, getCollectionName(reqBean.getCompanyId()));
		// WriteResult wr = getMongoTemplate().updateFirst(query, update,
		// getCollectionName(reqBean.getCompanyId()));
		if (wr == null)
			return false;
		else {
			respBean.setCount(wr.getN());
			respBean.setUpdateTime(now);
			return true;
		}
	}

	public SecretaryModel getById(String companyId, String _id) throws SzException {
		return (SecretaryModel) getMongoTemplate().findOne(new Query(new Criteria("_id").is(new ObjectId(_id))),
				getClassSon(), getCollectionName(companyId));
	}

	private void criteriaNotifyType(Criteria c, Integer notifyType) {
		if (notifyType != null) {
			if (notifyType == SecretaryModel.NotifyType_Both) {
				// c.and("notifyType").is(notifyType);
			} else {
				c.and("notifyType").in(notifyType, SecretaryModel.NotifyType_Both);
			}
		}
	}

	private Criteria getBadgeByTypeCriteria(Criteria childUserC, String fieldName, ReqSecretaryTypeBadgeBean reqBean,
			List<Integer> queryTypes) {
		Criteria c = new Criteria();
		c.and("users").elemMatch(childUserC);
		criteriaNotifyType(c, reqBean.getNotifyType());
		c.and(fieldName).in(queryTypes);
		return c;
	}

	private RespBriefReportTypeBadgeBean getBadgeByTypesBriefReport(String fieldName, ReqSecretaryTypeBadgeBean reqBean,
			String type) throws SzException {
		SecretaryModel model = findOneByParams(fieldName, reqBean, type);
		if (model == null) {
			return null;
		}
		RespBriefReportTypeBadgeBean respBean = new RespBriefReportTypeBadgeBean();
		respBean.setBriefReport((BriefReportModel) model);
		int count = commonGetBadgeByTypesCount(fieldName, reqBean, type);
		respBean.setCount(count);
		return respBean;
	}

	private SecretaryModel findOneByParams(String fieldName, ReqSecretaryTypeBadgeBean reqBean, String type)
			throws SzException {
		List<Integer> queryTypes = commonQueryTypes(type);
		if (queryTypes == null || queryTypes.isEmpty())
			return null;
		Criteria childUserC = new Criteria("userId").is(reqBean.getUserId());
		Criteria c = getBadgeByTypeCriteria(childUserC, fieldName, reqBean, queryTypes);
		Query query = new Query(c);
		Sort sort = new Sort(new Sort.Order(Direction.DESC, "createTime"),
				new Sort.Order(Direction.DESC, "updateTime"));
		query.with(sort);
		SecretaryModel model = (SecretaryModel) getMongoTemplate().findOne(query, getClassSon(),
				getCollectionName(reqBean.getCompanyId()));
		if (model == null)
			return null;
		tidySecretaryByUser(model, reqBean.getUserId());
		return model;
	}

	private List<Integer> commonQueryTypes(String type) {
		if (StringUtils.isEmpty(type))
			return null;
		String[] types = type.split(",");
		List<Integer> queryTypes = new ArrayList<>(types.length);
		for (String t : types) {
			if (StringUtils.isEmpty(t))
				continue;
			queryTypes.add(Integer.valueOf(t));
		}
		return queryTypes;
	}

	private int commonGetBadgeByTypesCount(String fieldName, ReqSecretaryTypeBadgeBean reqBean, String type)
			throws SzException {
		List<Integer> queryTypes = commonQueryTypes(type);
		if (queryTypes == null || queryTypes.isEmpty())
			return 0;
		Criteria childUserC = new Criteria("userId").is(reqBean.getUserId());
		childUserC.and("isRead").is(0);
		childUserC.and("isDel").is(0);
		Criteria c = getBadgeByTypeCriteria(childUserC, fieldName, reqBean, queryTypes);
		Query query = new Query(c);
		int count = (int) getMongoTemplate().count(query, getClassSon(), getCollectionName(reqBean.getCompanyId()));
		return count;
	}

	private RespSecretaryTypeBadgeBean getBadgeByTypes(String fieldName, ReqSecretaryTypeBadgeBean reqBean, String type)
			throws SzException {
		SecretaryModel model = findOneByParams(fieldName, reqBean, type);
		if (model == null) {
			return null;
		}
		RespSecretaryTypeBadgeBean respBean = new RespSecretaryTypeBadgeBean();
		respBean.setSecretary(model);
		int count = commonGetBadgeByTypesCount(fieldName, reqBean, type);
		respBean.setCount(count);
		return respBean;
	}

	private void tidySecretaryByUser(SecretaryModel model, String userId) {
		if (model == null)
			return;
		// 空或只有一个，不需要清理
		if ((model.getUsers() == null) || (model.getUsers().size() <= 1))
			return;

		MqTargetUserBean user = null;
		for (MqTargetUserBean u : model.getUsers()) {
			if (u.getUserId().equals(userId)) {
				user = u;
				break;
			}
		}
		model.getUsers().clear();
		model.getUsers().add(user);
	}

	public List<SecretaryModel> listUpdateObjects(ReqListSecretaryBean reqList) throws SzException {
		Criteria c = new Criteria();
		c.and("isDel").is(0);
		Criteria usersElem = new Criteria("userId").is(reqList.getUserId());
		usersElem.and("isDel").is(0);
		if (reqList.getIsRead() != null) {
			usersElem.and("isRead").is(reqList.getIsRead());
		}

		if ((reqList.getSourceType() != null) && (reqList.getSourceType() != 0))
			c.and("sourceType").is(reqList.getSourceType());
		if ((reqList.getModuleType() != null) && (reqList.getModuleType() != 0)) {
			c.and("type").is(reqList.getModuleType());
		}
		criteriaNotifyType(c, reqList.getNotifyType());
		Query query = new Query(c);
		if (reqList.getDirect() == ReqListSecretaryBean.Direct_New) {
			usersElem.and("updateTime").gt(reqList.getTimestamp());
			query.with(new Sort(new Sort.Order(Direction.ASC, "users.updateTime")));
		} else {
			usersElem.and("createTime").lt(reqList.getTimestamp());
			query.with(new Sort(new Sort.Order(Direction.DESC, "users.createTime")));
		}
		c.and("users").elemMatch(usersElem);
		query.skip(reqList.getSkip());
		if (reqList.getLimit() > 0)
			query.limit(reqList.getLimit());
		// query.withHint("isDel:1,users.updateTime:1,users.userId:1,users.isDel:1");
		List<SecretaryModel> models = getMongoTemplate().find(query, getClassSon(),
				getCollectionName(reqList.getCompanyId()));
		for (SecretaryModel model : models) {
			tidySecretaryByUser(model, reqList.getUserId());
		}
		return models;
	}

	public long getMaxUpdateTime(String compId, String userId, Integer isRead) throws SzException {
		Criteria c = new Criteria();
		Criteria usersElem = new Criteria("userId").is(userId);
		usersElem.and("isDel").is(0);
		if (isRead != null) {
			usersElem.and("isRead").is(isRead);
		}
		c.and("users").elemMatch(usersElem);
		c.and("isDel").is(0);
		Query query = new Query(c);
		query.with(new Sort(new Sort.Order(Direction.DESC, "users.updateTime")));
		SecretaryModel model = (SecretaryModel) getMongoTemplate().findOne(query, getClassSon(),
				getCollectionName(compId));
		if (model == null) {
			return 0l;
		}
		List<MqTargetUserBean> users = model.getUsers();
		if (users == null) {
			return 0L;
		}
		for (MqTargetUserBean user : users) {
			if (user.getUserId().equals(userId)) {
				return user.getUpdateTime();
			}
		}
		return 0L;
	}

	public void updateExtra(String id, String extra, String compId) throws SzException {
		Criteria c = new Criteria();
		c.and("_id").is(id);
		Query query = new Query(c);

		Update update = new Update();
		update.set("extra", extra);
		getMongoTemplate().updateFirst(query, update, getClassSon(), getCollectionName(compId));
	}

	/**
	 * 设置已读取 返回false 则表示没有更新数据，否则表示系统插入数据
	 * 
	 * @throws SzException
	 */
	public boolean updateUserRead(RespMessageSetReadForTypeBean resp, Integer[] extraIds, Integer[] sourceType,
			String userId, String companyId) throws SzException {
		long updateTime = resp.getUpdateTime();
		if (updateTime == 0L) {
			updateTime = System.currentTimeMillis();
		}
		Query query = new Query();
		Criteria c = new Criteria();
		c.and(SecretaryModel.FIELDNAME_extraBean + "." + CommonExtraBean.FIELDNAME_id).in(extraIds);
		c.and(SecretaryModel.FIELDNAME_sourceType).in(sourceType);

		Criteria cc = new Criteria(MqTargetUserBean.FIELDNAME_userId).is(userId);
		cc.and(MqTargetUserBean.FIELDNAME_isRead).is(MqTargetUserBean.isRead_no);
		c.and(SecretaryModel.FIELDNAME_users).elemMatch(cc);
		query.addCriteria(c);

		Update update = new Update();
		// long now = System.currentTimeMillis();
		update.set(SecretaryModel.FIELDNAME_users + ".$." + MqTargetUserBean.FIELDNAME_isRead,
				MqTargetUserBean.isRead_read);
		update.set(SecretaryModel.FIELDNAME_users + ".$." + MqTargetUserBean.FIELDNAME_readTime, updateTime);
		update.set(SecretaryModel.FIELDNAME_users + ".$." + MqTargetUserBean.FIELDNAME_updateTime, updateTime);
		WriteResult result = getMongoTemplate().updateMulti(query, update, getClassSon(), getCollectionName(companyId));
		if (result != null && result.getN() > 0) {
			resp.setCount(result.getN());
			resp.setUpdateTime(updateTime);
			return true;
		}
		return false;
	}

	public List<T> getUsers(long createTime, int type, String companyId) throws SzException {
		Query query = new Query();
		Criteria c = new Criteria();
		c.and(SecretaryModel.FIELDNAME_extraBean + ".type").is(type);
		long startTime = DateHelper.getStart();
		c.and(SecretaryModel.FIELDNAME_createTime).gt(startTime).lt(createTime);
		query.addCriteria(c);
		query.fields().include(SecretaryModel.FIELDNAME_users);
		return getMongoTemplate().find(query, getClassSon(), getCollectionName(companyId));
	}

	protected Class getClassSon() {
		return SecretaryModel.class;
	}
}
